package hyl.prod.task;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

import com.alibaba.fastjson2.JSONArray;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import hyl.core.MyDate;
import hyl.core.MyFun;
import hyl.core.db.MyDB;
import hyl.core.reflect.MyRef2;
import hyl.core.reflect.MyReflect;

/**
 * @className: TimerDemo
 * @description:
 * @author: zoudyaou qq:37798955
 * @date: 2018年5月10日 上午9:22:57
 * @copyright: 2018 zoudayou Inc. All rights reserved. 注意：本内容版权归邹达有先生
 *             所有，禁止未经授权以及用于其他的商业目
 */
public class TaskBean extends Thread {
	////////////////////////////// 公共属性///////////////////////////
	// 任务标题
	private String _title = null;
	// 生效时间
	private Date _starttime = null;
	// 结束时间
	private Date _endtime = null;
	// 依赖任务列表
	private String[] _rels = null;
	private int[] _超时 = new int[] { 10, 10, 10 };
	static final int I日间隔秒 = MyDate.S_DAY;
	// 下一次运行间隔
	private int _下一次运行间隔秒 = 0;
	// 运行时长
	private Long _runseconds = 0L;
	// 重试间隔
	private int _重试间隔豪秒 = 1000;
	// 最大重试次数
	private int _最大重试次数 = 0;
	protected TaskLog _Tlog = null;
	private boolean _异常后终止 = true;
	private String 时段格式 = "HH:mm";
	private String 整型时段格式 = "HHmm";

	public TaskBean(String title) {
		_title = title;
		_Tlog = new TaskLog(title);
	}

	class C时间段 {
		int _开始 = -1, _结束 = -1;

		C时间段(JSONArray 时间段) {
			if (时间段 == null)
				return;
			int len = 时间段.size();
			if (len >0)
				_开始 = MyFun.time2Int(MyFun.str2Date(时间段.getString(0), 时段格式), 整型时段格式);
			if (len ==1)
				_结束= _开始 ;
			else if (len == 2)
				_结束 = MyFun.time2Int(MyFun.str2Date(时间段.getString(1), 时段格式), 整型时段格式);
		}
	}

	public boolean is有效时段(Date cdate) {
		int stime = MyFun.time2Int(cdate, 整型时段格式);
		boolean flag = false;
		if (_时段表.size() == 0) {
			flag = true;
		} else {
			for (C时间段 区间 : _时段表) {
				if (区间._结束 == -1 && 区间._开始 == -1) {
					continue;
				} else if (区间._结束 == -1 && 区间._开始 > 0) {
					if (stime >= 区间._开始) {
						flag = true;
						break;
					}
				} else if (区间._结束 > 0 && 区间._开始 == -1) {
					if (stime <= 区间._结束) {
						flag = true;
						break;
					}
				} else {
					if (区间._开始 > 区间._结束) {
						if (stime <= 区间._结束) {
							flag = true;
							break;
						}
						if (stime >= 区间._开始) {
							flag = true;
							break;
						}
					} else {
						if (stime >= 区间._开始 && stime <= 区间._结束) {
							flag = true;
							break;
						}
					}
				}
			}
		}
		// MyFun.print(flag);
		return flag;
	}

	private List<C时间段> _时段表 = new ArrayList<C时间段>();

	public TaskLog getTlog() {
		return _Tlog;
	}

	public String getTitle() {
		return _title;
	}

	public void set时间区间(JSONArray 区间) {
		区间.forEach((m) -> {
			JSONArray jsonArray = (JSONArray) m;
			if (jsonArray.size() > 0) {
				C时间段 qj = new C时间段(jsonArray);
				// System.out.println(qj._开始+" "+qj._结束);
				_时段表.add(qj);
			}
		});

	}

	/**
	 * 默认是 每隔1s重试
	 * 
	 * @param 次数
	 */
	public void set重试间隔秒(Object 间隔秒) {
		if (_最大重试次数 == 0)
			return;
		int sec = MyFun.obj2Int(间隔秒, 0);
		int k = (int) _下一次运行间隔秒 / _最大重试次数;
		if (sec > k)
			sec = k;
		_重试间隔豪秒 = sec * 1000;
	}

	/**
	 * 默认是 0次
	 * 
	 * @param 次数 重试次数
	 */
	public void set重试次数(Object 次数) {
		_最大重试次数 = MyFun.obj2Int(次数, 0);
	}

	public void set超时(JSONArray 超时) {
		if (超时 == null)
			return;
		for (int i = 0; i < 超时.size(); i++) {
			_超时[i] = 超时.getIntValue(i);
		}

	}

	/**
	 * 默认是 5次
	 * 
	 * @param 次数
	 */
	public void set异常后终止(Object obj) {
		// System.out.println(obj);
		_异常后终止 = MyFun.obj2Bool(obj, true);
		// System.out.println(_异常后终止);
	}

	String _成功字符;

	public void set成功字符(Object 字符) {
		_成功字符 = MyFun.obj2Str(字符);
	}

	public String get成功字符() {
		return _成功字符;
	}

	public Long getRunseconds() {
		return _runseconds;
	}

	public void setRunseconds(Long runseconds) {
		this._runseconds = runseconds;
	}

	// 约定只能从当前时间开始执行
	public Date getStarttime() {
		if (_starttime == null)
			_starttime = new Date();
		else if (_starttime.before(new Date()))
			_starttime = MyDate.getEffDateOfNext(_starttime, this._下一次运行间隔秒);
		return _starttime;
	}

	public void setStarttime(Date starttime) {
		this._starttime = starttime;
	}

	public Date getEndtime() {
		return _endtime;
	}

	public void setEndtime(Date endtime) {
		this._endtime = endtime;
	}

	public String[] getRels() {
		return _rels;
	}

	public void set依赖任务(String rel) {
		if (rel == null || rel.isEmpty())
			return;
		_rels = rel.split(",");
		_Tlog.setRels(_rels);
	}

	public void set下一次运行间隔秒(Object 间隔时长) {
		this._下一次运行间隔秒 = MyFun.obj2Int(间隔时长, I日间隔秒);
	}

	////////////////////////// 特殊模式 专有属性 //////////////////////
	private Object[] _params = null;
	// http 调用模式 的url
	private String _httpurl = null;
	// jar 模式 调用外部jar包 并检索主类 jar#class
	private String _jarFile = null;
	private String _jarClass = null;
	private String _jarMethod = null;
	public boolean _IsStaticFun = false;// 默认是非静态方法
	// cmd 调用模式 输入bat 脚本文件名
	private String _command = null;
	// sql 调用模式
	private String _sql = null;
	// private Object[] sqlParams = null;
	// cmd 调用模式 输入bat 脚本文件名
	private String _db = null;

	public String getCmd() {
		return _command;
	}

	public void setCmd(String command) {
		this._command = MyFun.trim2(command);
	}

	public String getUrl() {
		return _httpurl;
	}

	public void setUrl(String url) {
		this._httpurl = MyFun.trim2(url);
	}

	public String getJarFile() {
		return _jarFile;
	}

	public void setJarFile(String jar) {
		this._jarFile = MyFun.trim2(jar);
	}

	public String get时段格式() {
		return 时段格式;
	}

	public void set时段格式(String 时段格式) {
		if (MyFun.isEmpty(时段格式))
			return;
		this.时段格式 = 时段格式;
		this.整型时段格式 = 时段格式.replace(" ", "").replace(":", "").replace("-", "").replace("/", "");
	}

	/**
	 * @return the jarclass
	 */
	public String getJarClass() {
		return _jarClass;
	}

	/**
	 * @param jarclass the jarclass to set
	 */
	public void setJarClass(String jarclass) {
		this._jarClass = MyFun.trim2(jarclass);
	}

	/**
	 * @return the jarMethod
	 */
	public String getJarMethod() {
		return _jarMethod;
	}

	/**
	 * @param jarMethod the jarMethod to set
	 */
	public void setJarMethod(String jarMethod) {
		this._jarMethod = MyFun.trim2(jarMethod);
	}

	public String getSql() {
		return _sql;
	}

	public void setSql(String sql) {
		this._sql = MyFun.trim2(sql);
	}

	public Object[] getParams() {
		return _params;
	}

	public void setParams(JSONArray ps) {
		if (ps == null || ps.size() == 0)
			return;
		this._params = ps.toArray();
	}

	public String getDb() {
		return _db;
	}

	public void setDb(String db) {
		if (MyFun.isEmpty(db))
			return;
		else {
			this._db = MyFun.trim2(db);
		}
	}

	OkHttpClient client = new OkHttpClient();

	boolean f检查结果(String msg) {
		
		if (MyFun.isEmpty(_成功字符)) {
			_Tlog.end(msg);
			return true;
		} else {
			if (msg.contains(_成功字符)) {
				_Tlog.end(msg);
				return true;
			} else {
				_Tlog.setErr(TaskLog.E反馈内容不正确, msg);
				return false;
			}
		}
	}

	///////////// 调用函数////////////////////////
	protected boolean runHttp(int 第几次) {
		try {
			_Tlog.begin(第几次);
			// 若 依赖任务
			OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure(false)
					.connectTimeout(_超时[0], TimeUnit.SECONDS) // 连接超时
					.readTimeout(_超时[1], TimeUnit.SECONDS) // 读取超时
					.writeTimeout(_超时[2], TimeUnit.SECONDS) // 写超时
					.build();
			Request request = new Request.Builder().url(_httpurl).build();
			Response response = client.newCall(request).execute();
			String msg = response.body().string().substring(0,2048);
//			String msg = MyHttp.getResult(MyHttp.sendGet(_httpurl)) ;
			return f检查结果(msg);
		} catch (Exception e) {
			_Tlog.setErr(TaskLog.Ehttp请求异常, e.getMessage());
			return false;
		}
	}

	protected boolean runCmd(int 第几次) {
		try {
			_Tlog.begin(第几次);
			Runtime runtime = Runtime.getRuntime();
			Process process;
			process = runtime.exec("cmd /c " + _command);
			BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
			String line = null;
			StringBuilder build = new StringBuilder();
			while ((line = br.readLine()) != null) {
				build.append(line);
			}

			return f检查结果(build.toString());
		} catch (IOException e) {
			_Tlog.setErr(TaskLog.Ecmd异常, e.getMessage());
			return false;
		}
	}

	protected boolean runJar(int 第几次) {
		try {
			_Tlog.begin(第几次);
			// System.out.println(this.getJarFile());
			// System.out.println(this.getJarClass());
			// System.out.println(this.getParams());
			Class<?> clazz = MyRef2.getJarClass(this.getJarFile(), this.getJarClass());
			Object response = null;
			if (this._IsStaticFun) {
				// 调用静态的方法
				if (MyFun.isEmpty(_jarMethod) || _jarMethod.equals("main"))
					// 如果没有 Method 自动调用main 方法
					response = MyRef2.invokeMain(clazz, this.getParams());
				else {
					// 如果有 Method 调用静态 method 方法
					response = MyRef2.invoke方法(clazz, _jarMethod, this.getParams());
				}
			} else {
				// 调用动态的方法
				Object obj = MyRef2.newInstance(clazz, null);
				MyReflect mr = new MyReflect(obj, clazz);
				if (MyFun.isEmpty(_jarMethod))
					response = mr.invoke("run", _params);//
				else
					response = mr.invoke(_jarMethod, _params);//
			}

			return f检查结果(MyFun.obj2Str(response));
		} catch (Exception e) {
			_Tlog.setErr(TaskLog.Ejar异常, e.getMessage());
			return false;
		}
	}

	protected boolean runSql(int 第几次) {
		try {
			_Tlog.begin(第几次);
			MyDB db1 = My.getDb(_db);
			long id = db1.executeUpdate(_sql, this._params);
			if (id < 0) {
				_Tlog.setErr(TaskLog.Esql异常, _sql);
				return false;
			}
			_Tlog.end("");
			return true;
		} catch (Exception e) {
			_Tlog.setErr(TaskLog.Esql异常, e.getMessage());
			return false;
		}
	}

	////////////// 调用层//////////////
	public void run() {
		// SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:SS");
		// String dateString = sdf.format(date.getTime());
		// System.out.println(dateString);
		// 得到定时器实例
		Timer t = new Timer();
		final TaskBean that = this;
		_Tlog.setPath(this._title);
		if (this._下一次运行间隔秒 < 2) {
			_Tlog.setErr(TaskLog.E间隔时间过短, "任务运行间隔不能小于2秒");
			return;
		}
		if (that.getEndtime() != null && this.getEndtime().compareTo(new Date()) < 0) {
			_Tlog.setErr(TaskLog.E任务到期, "任务截止时间已经过期！");
			return;
		}
		// System.out.println("kaishi1:"+MyFun.getNow());
		Date start = this.getStarttime();

		TimerTask timerTask = new TimerTask() {
			public void run() {
				int cc = 0;
				while (true) {
					Date cdate = new Date();

					if (that.getEndtime() != null && that.getEndtime().compareTo(cdate) < 0) {
						_Tlog.setErrTimeOut();
						throw new RuntimeException();
					}

					if (!is有效时段(cdate)) {// 当前时间不在所有区间中
						return;// 那么退出
					}
					checkrel(); // 阻塞模式 阻止后续程序运行 只可能返回
					boolean 状态 = false;
					if (that.getUrl() != null) {
						状态 = that.runHttp(cc);
					} else if (that.getCmd() != null) {
						状态 = that.runCmd(cc);
					} else if (that.getJarFile() != null && that.getJarClass() != null) {
						状态 = that.runJar(cc);
					} else if (that.getSql() != null && that.getDb() != null) {
						状态 = that.runSql(cc);
					} else {
						状态 = true;
						_Tlog.setErr(TaskLog.E缺少配置项, "在app.conf文件中,配置缺少必填项");
					}
					if (状态)
						return;
					cc++;
					// System.out.println(cc);
					if (cc <= _最大重试次数) {
						// System.out.println(cc);
						MyDate.sleep(_重试间隔豪秒);
					} else {
						// System.out.println("ccc"+cc+_异常后终止);
						if (_异常后终止)
							this.cancel();
						return;
					}

				}
			}
		};
		long jg = 1000L * _下一次运行间隔秒;
		// System.out.println(_下一次运行间隔秒);
		// System.out.println(jg);
		// 使用匿名内方式进行方法覆盖
		t.scheduleAtFixedRate(timerTask, start, jg);
	}

	// -------------------------- 检查依赖关系------------------------
	// 返回true 表示依赖的任务没有异常, 可以运行当前任务了
	// 返回false 表示依赖的任务异常, 还不能运行当前任务
	void checkrel() {
		String msg = "";
		// 如果没有依赖任务 返回true
		if (_rels == null || _rels.length == 0)
			return;
		// 检查依赖任务
		while (true) {// 只要有一个任务异常就阻塞,不允许继续执行程序
			boolean flag = true;
			// 遍历所有依赖任务
			for (String tit : this._rels) {
				TaskBean retu = TaskServer.config.get(tit);
				if (retu == null)
					continue;// 依赖任务不存在,查看下一条依赖
				if (retu.getTlog().isErr()) {// 如果依赖的任务异常
					msg = "异常!依赖任务=" + tit;
					_Tlog.setErr(TaskLog.E依赖异常, msg);
					flag = false;
					break;
				}
			}
			if (flag)
				return;
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
			}
		}
	}
}